package cn.gwm.flink.streaming.dws.reservecharge;

import cn.gwm.flink.streaming.constant.BaseFields;
import cn.gwm.flink.streaming.dwm.charge.model.ChargeModelEnum;
import cn.gwm.flink.streaming.dwm.charge.util.SupplyUtil;
import cn.gwm.flink.streaming.dws.reservecharge.beans.RcElectricConfigDao;
import cn.gwm.flink.streaming.dws.reservecharge.beans.RcElectricConfigSeasonDao;
import cn.gwm.flink.streaming.dws.reservecharge.beans.RcElectricConfigValleyPeakDao;
import cn.gwm.flink.streaming.dws.reservecharge.enums.ChargeConnectTypeEnum;
import cn.gwm.flink.streaming.dws.reservecharge.enums.ChargeUseElectricTypeEnum;
import cn.gwm.flink.streaming.dws.reservecharge.enums.ElectricExecuteValleyPeakTypeEnum;
import cn.gwm.utils.DateTimeUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author Liu.Nanfang
 * @date 2022/8/15 11:29
 * @description
 */
@Slf4j
public class ChargePrice {
    private static final ChargePrice instance = new ChargePrice();

    private CopyOnWriteArrayList<RcElectricConfigDao> priceList = new CopyOnWriteArrayList<>();

    private ChargePrice() {
        System.out.println("初始化电价信息");
    }

    public static ChargePrice getInstance() {
        System.out.printf("-----get instace \n");
        return instance;
    }

    public static void main(String[] args) throws Exception {
        ChargePrice chargePrice = ChargePrice.getInstance();
        chargePrice.getChargePriceInfoList();
        // 广东： 440103 山东：370100 大连：210200 宝鸡：610302 江阳：441702 610116
        RcElectricConfigDao data = chargePrice.getFilterByAdCode("530581", ChargeUseElectricTypeEnum.RESIDENTS.getCode());

        System.out.println("source rcConfig data : " + JSON.toJSON(data));
//        生成标准化数据结构
        ReserveChargeElectricConfig resultConf = chargePrice.getReserveChargeElectricConfig(data);
        System.out.println("Conversion resultConf : " + JSON.toJSON(resultConf));
        ReserveChargeData reserveChargeData = new ReserveChargeData();
//        reserveChargeData.setItem_time(1680975305000L);
        reserveChargeData.setItem_time(DateUtil.parse("2023-03-31 20:17:55").getTime());
        Date start = DateUtil.parse("2023-03-31 20:17:55");
        Date end = DateUtil.parse("2023-04-01 08:27:21");

        reserveChargeData.setBusiStartChargeTime(start.getTime());
        reserveChargeData.setBusiEndChargeTime(end.getTime());
        reserveChargeData.setChargeStatus("6");
        reserveChargeData.setDiscountPower(21.0);
        reserveChargeData.setChargeConnectType(ChargeConnectTypeEnum.SLOW.getCode());
        reserveChargeData.setChargeModel(Integer.valueOf(ChargeModelEnum.CHARGE_SMART.getCode()));

        ChargePrice.setElectricPrice(resultConf, reserveChargeData);
        BuChargeHandle.chargeModelTypeHandle(reserveChargeData, resultConf);
        ChargePrice.computerChargePowerAmount(reserveChargeData);
        System.out.println("out print reserveChargeData = " + JSON.toJSON(reserveChargeData));
    }

    /**
     * 获取电价列表
     *
     * @return
     */
    public List<RcElectricConfigDao> getChargePriceInfoList() {
        if (priceList.size() == 0) {
            List<RcElectricConfigDao> newList = getHttpPrice();
            priceList.addAll(newList);
        }
        return priceList;
    }

    /**
     * 安全更新电价配置
     */
    public void updateChargePriceInfoList() {
        List<RcElectricConfigDao> updateList = getHttpPrice();
        if (updateList.size() > 0 && updateList.size() > 0) {
            priceList.clear();
            priceList.addAll(updateList);
        }
    }

    public static Integer provinceCodeByAdCode(String adCode) {
        if (StringUtils.isNotBlank(adCode)) {
            Integer districtCode = Integer.valueOf(adCode);
            Integer provinceCode = (int) Math.floor(districtCode / 10000) * 10000;
            return provinceCode;
        }
        return null;
    }

    public static Integer cityCodeByAdCode(String adCode) {
        if (StringUtils.isNotBlank(adCode)) {
            Integer districtCode = Integer.valueOf(adCode);
            Integer cityCode = (int) Math.floor(districtCode / 100) * 100;
            return cityCode;
        }
        return null;
    }


    /**
     * 设置电价信息查
     *
     * @param conf
     * @param nowData
     */
    public static void setLowAndHighPeakPrice(ReserveChargeElectricConfig.ElectricConfig conf, ReserveChargeData nowData) {
        if (conf.getFlatPeak() != null && conf.getFlatPeak() != 0) {
            nowData.setFlatPeakPrice(BigDecimal.valueOf(conf.getFlatPeak())
                    .setScale(4, BigDecimal.ROUND_HALF_UP));
            if (conf.getNotOpen() != null && conf.getNotOpen() != 0) {
                nowData.setNonTimePrice(BigDecimal.valueOf(conf.getNotOpen())
                        .setScale(4, BigDecimal.ROUND_HALF_UP));
            }

        } else if (conf.getNotOpen() != null && conf.getNotOpen() != 0) {
            nowData.setFlatPeakPrice(BigDecimal.valueOf(conf.getNotOpen())
                    .setScale(4, BigDecimal.ROUND_HALF_UP));

            nowData.setNonTimePrice(BigDecimal.valueOf(conf.getNotOpen())
                    .setScale(4, BigDecimal.ROUND_HALF_UP));
        } else {
            nowData.setFlatPeakPrice(new BigDecimal("0"));
            nowData.setNonTimePrice(new BigDecimal("0"));
        }
        nowData.setLowPeakPrice(conf.getLowPeak() != null ? BigDecimal.valueOf(conf.getLowPeak())
                .setScale(4, BigDecimal.ROUND_HALF_UP) : new BigDecimal("0"));
        nowData.setHighPeakPrice(conf.getHighPeak() != null ? BigDecimal.valueOf(conf.getHighPeak())
                .setScale(4, BigDecimal.ROUND_HALF_UP) : new BigDecimal("0"));
    }

    /**
     * 根据电价配置信息设置相关信息
     *
     * @param configDao
     * @param nowData
     */
    public static void setElectricPrice(ReserveChargeElectricConfig configDao, ReserveChargeData nowData) {
        try {
            DateTime currentDate = DateUtil.date(nowData.getItem_time());
            System.out.println("currentDate.fo = " + DateUtil.format(currentDate, "yyyy-MM-dd HH:mm:ss"));
            // 电价获取原则：取第二阶梯 低谷与不分时的价格差价 与优惠电量的乘积
            System.out.println("setElectricPrice configDao:" + JSON.toJSON(configDao));
            configDao.getElectricConfigs().forEach(conf -> {
                if ((nowData.getLowPeakPrice() != null && nowData.getLowPeakPrice().compareTo(BigDecimal.ZERO) > 0) ||
                        (nowData.getHighPeakPrice() != null && nowData.getHighPeakPrice().compareTo(BigDecimal.ZERO) > 0)
                ) {
                    return;
                }
                // 存在分季
                if (configDao.getSeasonType() != 1 && isSeasonTypeList(conf.getRcElectricConfigSeasonParamList(), currentDate)) {
                    if (configDao.getValleyPeak() == 1 && conf.getLowPeak() != 0) {
                        setValleyPeak(conf.getRcElectricConfigValleyPeakParamList(), nowData);
                        setLowAndHighPeakPrice(conf, nowData);
                    } else {
                        nowData.setFlatPeakPrice(BigDecimal.valueOf(conf.getNotOpen())
                                .setScale(4, BigDecimal.ROUND_HALF_UP));
                        nowData.setLowPeakPrice(new BigDecimal("0"));
                        nowData.setHighPeakPrice(new BigDecimal("0"));
                    }

                } else if (configDao.getSeasonType() == 1 && conf.getRcElectricConfigValleyPeakParamList().size() > 0) {
                    setValleyPeak(conf.getRcElectricConfigValleyPeakParamList(), nowData);
                    setLowAndHighPeakPrice(conf, nowData);

                } else {
                    nowData.setFlatPeakPrice(BigDecimal.valueOf(conf.getNotOpen())
                            .setScale(4, BigDecimal.ROUND_HALF_UP));
                }

            });
            System.out.println("setElectricPrice JSON.TO = " + JSON.toJSON(nowData));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("setElectricPrice err: {}", e.toString());
        }

    }

    /**
     * 计算充电优惠金额
     * 云端收到PDCU发送的充电量，充电时长信号后，应计算本次智能预约充电相较于立即充电节省的费用和智能预约充电的充电时间:
     * 智能预约充电相较立即充电节省的费用计算方法：智能预约充电与立即充电在波谷段的充电量差值 * 非谷段与谷段的电价差值，
     * 智能预约充电与立即充电在波谷段的充电量差值=（智能预约充电的波谷充电时长 - 立即充电的波谷时段充电时长 ）* （智能预约充电的波谷充电量/智能预约充电的波谷充电时长）;
     * 优惠电价= 优惠电量*（波峰电价-波谷电价）
     *
     * @param nowData
     */
    public static void computerChargePowerAmount(ReserveChargeData nowData) {
        if (!ChargeConnectTypeEnum.SLOW.getCode().equals(nowData.getChargeConnectType())) {
            return;
        }
        if (nowData.getHighPeakPrice() != null && nowData.getLowPeakPrice() != null &&
                nowData.getDiscountPower() != null && !Double.valueOf(BaseFields.defaultVal).equals(nowData.getDiscountPower())) {
            BigDecimal diff;

            // 如果存在不分时电价，用不分时电价 - 峰谷电价
            if (nowData.getNonTimePrice() != null && nowData.getNonTimePrice().compareTo(BigDecimal.ZERO) > 0) {
                diff = nowData.getNonTimePrice().subtract(nowData.getLowPeakPrice());
            } else {
                diff = nowData.getHighPeakPrice().subtract(nowData.getLowPeakPrice());
            }

            if (diff.floatValue() > 0) {
                BigDecimal discountAmount = diff.multiply(new BigDecimal(nowData.getDiscountPower()))
                        .setScale(4, BigDecimal.ROUND_HALF_UP);
                nowData.setDiscountAmount(discountAmount);
            } else {
                nowData.setDiscountAmount(new BigDecimal(0));
            }
        }
    }

    /**
     * 判断是否存在分季
     *
     * @param list
     * @param currentDate
     * @return
     */
    public static boolean isSeasonTypeList(List<RcElectricConfigSeasonDao> list, DateTime currentDate) {
        long seasonCount = list.stream().filter(item -> {
            Date[] dateArr = DateTimeUtil.getStartDateAndEndDate(DateUtil.date(item.getStartDate()), DateUtil.date(item.getEndDate()));
            Date now1 = DateTimeUtil.getDateOffsetYear(currentDate);
//            System.out.println(item.getSeasonType() + ":::" + now1 + "=== " + dateArr[0] + "=== " + dateArr[1]);
            boolean result = DateUtil.isIn(now1, dateArr[0], dateArr[1]);
            return result;
        }).count();
        return seasonCount > 0;
    }

    /**
     * 设置峰谷时间
     *
     * @param list
     * @param nowData
     */
    public static void setValleyPeak(List<RcElectricConfigValleyPeakDao> list, ReserveChargeData nowData) {
        list.forEach(item -> {
            if (ElectricExecuteValleyPeakTypeEnum.LOW.getKey().equals(item.getValleyPeakType())) {
                nowData.setOffPeakTime(String.format("%s-%s", item.getStartTime(), item.getEndTime()));
            }
            if (ElectricExecuteValleyPeakTypeEnum.HIGH.getKey().equals(item.getValleyPeakType())) {
                nowData.setOnPeakTime(String.format("%s-%s", item.getStartTime(), item.getEndTime()));
            }
        });
    }

    public List<RcElectricConfigDao> getHttpPrice() {
        System.out.println("初始化电价信息调用api\n");
        String text = SupplyUtil.getChargePriceInfoList();
        if (StringUtils.isNotBlank(text)) {
            List<RcElectricConfigDao> newList = JSON.parseArray(text, RcElectricConfigDao.class);
            return newList;

        }
        return null;
    }

    public RcElectricConfigDao getFilterByAdCode(String adCode) {
        Integer ad = cityCodeByAdCode(adCode);
        if (ad == null) {
            return null;
        }
        for (RcElectricConfigDao item : priceList) {
            Long count = item.getRcElectricConfigCityParamList().stream().filter(j -> j.getAreaCode().equals(String.valueOf(ad))).count();
            if (count > 0) {
                return item;
            }
        }
        return null;
    }

    /**
     * 根据 adCode 获取所有用电类型的电价信息
     *
     * @param adCode
     * @return
     */
    public List<RcElectricConfigDao> getFilterListByAdCode(String adCode) {
        Integer ad = cityCodeByAdCode(adCode);
        List<RcElectricConfigDao> list = new ArrayList<>();
        if (ad == null) {
            return list;
        }
        for (RcElectricConfigDao item : priceList) {
            Long count = item.getRcElectricConfigCityParamList().stream().filter(j -> j.getAreaCode().equals(String.valueOf(ad))).count();
            if (count > 0) {
                list.add(item);
            }
        }
        return list;
    }

    /**
     * 获取电价配置
     *
     * @param adCode
     * @param useElectricType 用电类型
     * @return
     */
    public RcElectricConfigDao getFilterByAdCode(String adCode, Integer useElectricType) {
        Integer ad = cityCodeByAdCode(adCode);
        if (ad == null) {
            return null;
        }
        for (RcElectricConfigDao item : priceList) {
            Long count = item.getRcElectricConfigCityParamList().stream().filter(j -> j.getAreaCode().equals(String.valueOf(ad))).count();
            if (count > 0 && item.getUseElectricType().equals(useElectricType)) {
                return item;
            }
        }
        return null;
    }

    /**
     * 根据adcoe 与 充电类型查找用电类型
     *
     * @param adCode
     * @param applicableScope
     * @return
     */
    public String getFilterByAdCodeAndScope(String adCode, String applicableScope) {
        Integer ad = cityCodeByAdCode(adCode);
        if (ad == null) {
            return null;
        }
        Optional<RcElectricConfigDao> useType = priceList.stream()
                .filter(item ->
                        item.getRcElectricConfigCityParamList().stream()
                                .filter(j -> j.getAreaCode().equals(String.valueOf(ad)))
                                .count() > 0)
                .filter(item -> item.getApplicableScope().contains(applicableScope))
                .findFirst();

        if (useType.isPresent()) {
            return String.valueOf(useType.get().getUseElectricType());
        }
        return null;
    }

    /**
     * 抽取删选电价信息 返回简单数据结构
     *
     * @param configDao 电价信息数据
     */
    public ReserveChargeElectricConfig getReserveChargeElectricConfig(RcElectricConfigDao configDao) {
        /**
         --电价定义，峰谷电价为0的表示该条记录的为不分时电价，不分时电价为0的表示该条记录的为峰谷电价
         rcElectricConfigInfoParamList:{
         "executeTime": "2022-06-30",--执行时间
         "flatPeak": 1.0,--平峰电价
         "highPeak": 3.0,--高峰电价
         "ladderType": 1,--阶梯（阶梯 0无阶梯 1第一阶梯 2第二阶梯 3第三阶梯 4第四阶梯）
         "lowPeak": 1.0,--低谷电价
         "notOpen": 2.0,--不分时电价
         "seasonType": 2--分季类型（1: 不区分 [ 2: 夏季 3: 分夏季 ] [ 4: 取暖季 5: 非取暖季 ] [ 6: 丰水期 7: 平水期 8: 枯水期 ]）
         }
         阶梯定义
         rcElectricConfigLadderParamList:{
         "endValue": 2000,--结束电量
         "executeWay": 1,--执行方式（1 年 2 月）
         "ladderType": 1,--阶梯类型（0无阶梯 1第一阶梯 2第二阶梯 3第三阶梯 4第四阶梯）
         "seasonType": 2,--分季类型（1: 不区分 [ 2: 夏季 3: 分夏季 ] [ 4: 取暖季 5: 非取暖季 ] [ 6: 丰水期 7: 平水期 8: 枯水期 ]）
         "startValue": 1000--开始电量

         }
         分季定义
         rcElectricConfigSeasonParamList:{
         "endDate": "2022-06-22",--结束月份
         "seasonType": 2,--分季类型（1: 不区分 [ 2: 夏季 3: 分夏季 ] [ 4: 取暖季 5: 非取暖季 ] [ 6: 丰水期 7: 平水期 8: 枯水期 ]）
         "startDate": "2022-06-17"--开始月份
         }
         峰谷定义
         rcElectricConfigValleyPeakParamList:{
         "endTime": "15:24",--结束时间
         "executeTime": "2022-06-28",--执行时间
         "seasonType": 2,--分季类型（1: 不区分 [ 2: 夏季 3: 分夏季 ] [ 4: 取暖季 5: 非取暖季 ] [ 6: 丰水期 7: 平水期 8: 枯水期 ]）
         "startTime": "14:24",--开始时间
         "valleyPeakType": 3--峰谷时段（ 1: 平峰 2: 低谷 3: 高峰）
         }
         *
         */
        ReserveChargeElectricConfig elConf = new ReserveChargeElectricConfig();
        elConf.setUseElectricType(configDao.getUseElectricType());
        elConf.setValleyPeak(configDao.getValleyPeak());
        elConf.setLadder(configDao.getLadder());
        elConf.setSeasonType(configDao.getSeasonType());
        elConf.setApplicableScope(configDao.getApplicableScope());
        elConf.setAreaCode(configDao.getAreaCode());
        List<ReserveChargeElectricConfig.ElectricConfig> confList = new ArrayList<>();
        elConf.setElectricConfigs(confList);
        configDao.getRcElectricConfigInfoParamList().stream()
                // 暂定获取第二阶梯或无阶梯
                .filter(item -> item.getLadderType() == 0 || item.getLadderType() == 2)
                .forEach(conf -> {
                    ReserveChargeElectricConfig.ElectricConfig electricConfig = new ReserveChargeElectricConfig.ElectricConfig();
                    BeanUtil.copyProperties(conf, electricConfig);
                    // 存在分季
                    if (configDao.getSeasonType() != 1) {
                        configDao.getRcElectricConfigSeasonParamList().stream()
                                .filter(s -> s.getSeasonType().equals(conf.getSeasonType()))
                                .forEach(item -> electricConfig.getRcElectricConfigSeasonParamList().add(item));
                    }
                    // 存在峰谷
                    if (configDao.getValleyPeak() == 1) {
                        long SeasonTypeCount = configDao.getRcElectricConfigValleyPeakParamList().stream().filter(s -> s.getSeasonType().equals(1)).count();
                        if (SeasonTypeCount == configDao.getRcElectricConfigValleyPeakParamList().stream().count()) {
                            configDao.getRcElectricConfigValleyPeakParamList().stream()
                                    .forEach(item -> electricConfig.getRcElectricConfigValleyPeakParamList().add(item));
                        } else {
                            // 山东特殊情况，存在峰谷与分季相关
                            configDao.getRcElectricConfigValleyPeakParamList().stream()
                                    .filter(s -> s.getSeasonType().equals(conf.getSeasonType()))
                                    .forEach(item -> electricConfig.getRcElectricConfigValleyPeakParamList().add(item));
                        }
                    }
                    // 存在阶梯
                    if (configDao.getLadder() == 1) {
                        configDao.getRcElectricConfigLadderParamList().stream()
                                .filter(s -> s.getSeasonType().equals(conf.getSeasonType()) && s.getLadderType().equals(conf.getLadderType()))
                                .forEach(item -> electricConfig.getRcElectricConfigLadderParamList().add(item));
                    }
                    confList.add(electricConfig);
                });
        System.out.println("confList = " + JSON.toJSON(elConf));
        return elConf;
    }


}
